package nl.rug.ai.believer.model;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Properties;

/**
 * A utility class providing support functions that should be available in the whole program, but do not really have
 * anything to do with any class.
 * @author S1849700
 *
 */
public class Util {

	static boolean verbose = false;
	static ArrayList<String> reservedKeywords = new ArrayList<String>();
	
	// Util contains a few constants used to evaluate.
	// If you add one, you must also add it's exact name to the allKeyWords[] array, and the config.ini
	static String turnstyle = "|=";
	static String or = " v ";
	static String and = "^";
	static String implies = "->";
	static String equals = "<=>";
	static String not = "~";
	static String belief = "B";
	static String knowledge = "K";
	// used for conditional beliefs
	static String conditional = "CONDITIONAL";
	// used for updates. 
	static String update = "UPDATE";
	static String radical_upgrade = "RADICAL_UPGRADE";
	static String conservative_upgrade = "CONSERVATIVE_UPGRADE";
	static String agentStartTag = "{";
	static String agentEndTag = "}";
	static String agentSeparator = ",";
	
	// used for reading a config file. These Strings *must* be identical to the field names above.
	private static String allKeyWords[] = {"turnstyle", "or", "and", "implies", "equals", "not",
		"belief", "knowledge", "conditional", "update", "radical_upgrade", "conservative_upgrade",
		"agentStartTag", "agentEndTag", "agentSeparator"};
	
	// static "constructor".
	static {
		try {
			for (String temp : allKeyWords) {
				// use reflection to obtain the fields matching allKeyWords
				Field field = Util.class.getDeclaredField(temp);
				// get the content of that field and add it to our array. This way we only need to add
				// new fields in 2 places.
				reservedKeywords.add((String)field.get(null));
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			// this "should" not go wrong unless someone broke the allKeyWords list.
			throw new Error("Internal failure, severe. If persistent, contact " +
					"the developer of this application.");
		}
	}
	
	
	/**
	 * This method prints debug text if verbose is set. It has no access modifier (default), and is this 
	 * public to all classes inside the package, but not outside.
	 * @param text The text to print.
	 */
	static void debug(String text) {
		if (verbose) {
			System.out.println(text);
		}
	}
	
	/**
	 * This method is responsible for reading the configuration file, and loading all values found there 
	 * into this class.
	 */
	static void loadConfig() {
		try {
			ArrayList<String> currentOperators = new ArrayList<String>();
			Properties myProps = new Properties();
			FileInputStream MyInputStream = new FileInputStream("config.ini");
			myProps.load(MyInputStream);
			for (String word : allKeyWords) {
				String value = myProps.getProperty(word);
				if (value == null ) {
					throw new RuntimeException("No value for: " + word); 
				}
				if (value.charAt(0) != '"' || value.charAt(value.length()-1) != '"') {
					throw new RuntimeException("Invalid value for: " + word);
				}
				value = value.substring(1, value.length()-1);
				if (value.trim().equals("")) {
					throw new RuntimeException("Invalid value for: " + word);
				}
				for (String temp : currentOperators) {
					// check if the new string is a subset of any existing, or vice versa.
					if (temp.indexOf(value) != -1 || value.indexOf(temp) != -1) {
						throw new RuntimeException("Value for \"" + word + "\" (" + value + 
								") is a subset of \"" + temp + "\" or vice versa"); 
					}
				}
				currentOperators.add(value);
			}
			// if we get here, all entries are there and valid.
			try {
				for (int i = 0; i < allKeyWords.length; i++) {
					// use reflection to obtain the fields matching allKeyWords
					Field field = Util.class.getDeclaredField(allKeyWords[i]);
					// overwrite security, allow us (through reflection only) to write non public fields.
					field.setAccessible(true);
					// write the field. First param is null because we write to a static field.
					field.set(null, currentOperators.get(i));
				}
			}
			catch (Exception e) { // this "should" not go wrong unless someone broke the allKeyWords list.
				e.printStackTrace();
				throw new Error("Internal failure, severe. If persistent, contact " +
						"the developer of this application.");
			}
			reservedKeywords = currentOperators;
		}
		catch (IOException ioe) {
			throw new RuntimeException("Could not read file: config.ini"); 
		}
	}
}
